home *** CD-ROM | disk | FTP | other *** search
/ Disc to the Future 2 / Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin / MAC / THINKC / 3_0 / FILEFORM / FILEFORM.C next >
Text File  |  1988-07-29  |  15KB  |  601 lines

  1.  
  2. /*
  3. stylefile.c -- C source code that defines (or more accurately proposes) a file format 
  4. standard for styled text that can be edited using the newer version of Apple's Text 
  5. Edit with selective styling, font changes and color changes.  
  6.  
  7. Developed to compile under THINK's Lightspeed C version 3.X, but should run fine under
  8. earlier version of LSC, and probably MPW╔
  9.  
  10. The basic file structure is:
  11.  
  12. Length                        Meaning
  13. ------                        -------
  14.  
  15. 40 bytes                    file header, see declaration below
  16.  
  17. textlength bytes            the un-styled ASCII text
  18.  
  19. stylelength    bytes            contents of a StScrpHandle as returned by GetStylScrap 
  20.  
  21.  
  22. Since every application that uses styled text probably has to save the same kind of
  23. information on disk, doesn't it make sense for all of us to do it the same way?
  24.  
  25. This format would be equivalent to the PICT file format for pictures.  Perhaps a good
  26. name for the format would be STXT for Styled TeXT?
  27.  
  28. Note that in the file header we leave room for four-byte selection offsets, and for
  29. lengths of the buffer text and the StScrpHandle.  In today's Text Edit, only two bytes
  30. are needed, but it probably is smart to waste a few bytes in case Apple ever really
  31. soups up TE to handle truly large blocks of text.
  32.  
  33. The following code, written to be compiled by Lightspeed C leaves no uncertainty about
  34. how the file format can be implemented.
  35.  
  36. If anyone is interested in discussing this proposal, please leave a message on CompuServe's
  37. APPDEV Forum.
  38.  
  39. Dave Winer 76244,120
  40.  
  41.  
  42. July 3, 1988 -- Release 2
  43. -------------------------
  44.  
  45. Added several features to the header, at the suggestion of Chris Wakefield [CompuServe
  46. 76337,335].
  47.  
  48. systemversionnumber is an integer that tells us what version of the system created
  49. the STXT file.  See IM-V pp 5-8.
  50.  
  51. vertscrollposition is a long that tells you how to initialize your vertical scrollbar
  52. so that it scrolls to the same position it was at when the STXT file was created.  The 
  53. Control Manager only deals with ints, but we made this a long to reserve space for
  54. growth.  You can ignore this if you don't dispay a vertical scrollbar, but at least
  55. set it to 0 when you save.
  56.  
  57. textoffset and styleoffset, both longs, say where the text and the style record are
  58. stored in the file.  This might allow you to store some other kinds of information
  59. in different places that will be ignored by programs reading STXT format.
  60.  
  61. windowrect says where the window that holds the text should be placed, in global
  62. coordinates.  If you don't save a real value here, be sure that windowrect.bottom
  63. is 0 -- it's probably safest to set all the fields of windowrect to 0.
  64.  
  65. Thanks Chris for some excellent suggestions!
  66.  
  67. A note -- even if a "standard" is never set, you can still write programs that read
  68. and write this file format.  I think you'll end up being happy that you did because
  69. it may give you some extra compatibility with some neat software products.  
  70.  
  71. No promises!
  72.  
  73. DW
  74.  
  75. July 29, 1988 -- Release 3
  76. --------------------------
  77.  
  78. At the suggestion of David Dunham, author of Acta and MiniWriter, I have cleaned up
  79. a few of the initializations in initeditbuffer.  I've also learned a little more about 
  80. TE and changed a couple of things at my own suggestion.
  81.  
  82. 1. Took out the code that inserts a blank, selects it, sets its style to the default
  83. and then deletes it.  This was voodoo, apparently very unnecessary.
  84.  
  85. 2. Instead of copying directly into the TEHandle's selStart and selEnd fields, it now
  86. uses TESetSelect as the exclusive way of working with these fields.  In general, you're
  87. supposed to use the procedural interface whenever one is provided.
  88.  
  89. 3. Included a routine that you might find generally useful: getscraphandle.
  90.  
  91. 4. Also put things in registers wherever possible.  This greatly reduces codesize!
  92.  
  93. 5. No changes to the file format, so currentversion stays at 2.
  94. */
  95.  
  96. #include "standard.h"
  97.  
  98.  
  99. #define currentversion 2
  100.  
  101. #define fname "\pSTXT Test File"
  102.  
  103.  
  104. typedef struct {
  105.  
  106.     int versionnumber; /*makes it easy to upgrade the format*/
  107.     
  108.     int systemversionnumber; /*what SysEnvirons returns, -1 indicates no version available*/
  109.     
  110.     long startsel, endsel; /*leave room a buffer of greater than 32767 characters*/
  111.     
  112.     long vertscrollposition; /*value for vertical scroll bar controlhandle*/
  113.     
  114.     long textoffset, textlength; /*where the text is stored, and its length*/
  115.     
  116.     long styleoffset, stylelength; /*where the style record is stored, and its length*/
  117.     
  118.     Rect windowrect; /*optional, the size of the window, if not set, set bottom to 0*/
  119.     } tystyleheader; 
  120.  
  121.  
  122. TEHandle editbuffer; /*the place where STXT files are saved from and loaded into*/
  123.  
  124. Rect editrect; /*the rectangle where text is displayed*/
  125.  
  126. WindowPtr editwindow; /*the window that the edit buffer is displayed in*/
  127.  
  128. #define automaticfont geneva
  129.  
  130. #define automaticsize 12
  131.  
  132. #define automaticface 0
  133.  
  134.  
  135. int getsystemversion () {
  136.     
  137.     SysEnvRec theworld;
  138.     
  139.     if (SysEnvirons (1, &theworld) != noErr) /*error, indicate no version number available*/
  140.         return (-1);
  141.         
  142.     return (theworld.systemVersion);
  143.     } /*getsystemversion*/
  144.     
  145.     
  146. boolean filewrite (fnum, ctwrite, bufptr) int fnum; long ctwrite; char *bufptr; {
  147.  
  148.     /*
  149.     write ctwrite bytes from bufptr to file indicated by fnum at
  150.     the FPos of the file.
  151.     */
  152.     
  153.     return (FSWrite (fnum, &ctwrite, bufptr) == noErr);
  154.     } /*filewrite*/
  155.     
  156.  
  157. boolean fileseek (fnum, pos) int fnum; long pos; {
  158.     
  159.     return (SetFPos (fnum, fsFromStart, pos) == noErr);
  160.     } /*fileseek*/
  161.     
  162.     
  163. boolean fileread (fnum, pos, ctread, bufptr) int fnum; long pos, ctread; char *bufptr; {
  164.  
  165.     /*
  166.     read ctread bytes from the file indicated by fnum into bufptr
  167.     at file position pos.
  168.     */
  169.     
  170.     if (!fileseek (fnum, pos))
  171.         return (false);
  172.         
  173.     return (FSRead (fnum, &ctread, bufptr) == noErr);
  174.     } /*fileread*/
  175.     
  176.  
  177. boolean initeditbuffer () {
  178.  
  179.     /*
  180.     create a new TEHandle in editbuffer.
  181.     
  182.     Apple Technote #131 says that the current port and font, size and style are 
  183.     important when TEStylNew is called.  
  184.     
  185.     editrect and editwindow must be set before this routine is called.
  186.     */
  187.  
  188.     TextStyle newstyle;
  189.     
  190.     SetPort (editwindow); /*TEStylNew makes a copy of the current port*/
  191.     
  192.     TextFont (automaticfont); /*see Apple Technote #131*/
  193.     
  194.     TextSize (automaticsize);
  195.     
  196.     TextFace (automaticface);
  197.     
  198.     editbuffer = TEStylNew (&editrect, &editrect);
  199.    
  200.     if (editbuffer == nil) /*allocation failed*/
  201.         return (false);
  202.     
  203.     TECalText (editbuffer); /*see Apple Technote #131*/
  204.     
  205.     TESetSelect (0, 0, editbuffer); /*get ready to set the nullstyle*/
  206.     
  207.     newstyle.tsFont = automaticfont;
  208.     
  209.     newstyle.tsSize = automaticsize;
  210.     
  211.     newstyle.tsFace = automaticface;
  212.       
  213.     TESetStyle (
  214.         doFont + doFace + doSize, &newstyle, true, editbuffer); /*see IM-V p 264*/
  215.  
  216.     return (true);
  217.     } /*initeditbuffer*/
  218.     
  219.     
  220. boolean validfileheader (header) tystyleheader header; {
  221.  
  222.     /*
  223.     put the file header through a vigorous reality check.
  224.     */
  225.     
  226.     if (header.versionnumber != currentversion)
  227.         return (false);
  228.         
  229.     if ((header.endsel < 0) || (header.endsel > header.textlength))
  230.         return (false);
  231.     
  232.     if (header.textlength < 0)
  233.         return (false);
  234.         
  235.     if (header.stylelength < 0)
  236.         return (false);
  237.         
  238.     return (true); /*passed all tests!!!*/
  239.     } /*validfileheader*/
  240.     
  241.     
  242. boolean loadstylerecord (fnum) int fnum; {
  243.  
  244.     /*
  245.     The caller wants us to read the contents of editbuffer from the open file "fnum".
  246.     
  247.     This effectively insulates the caller from detailed knowledge of both the structure
  248.     of the stylerecord and the details of our file format.  It also makes it easy for
  249.     a compound style of document, that might consist of many styled buffers, to be saved.
  250.     
  251.     If we return true then the operation went smoothly.  If we return false, there
  252.     was an error in reading from disk.  
  253.     */
  254.     
  255.     tystyleheader header;
  256.     Handle htmp;
  257.     StScrpHandle hstyle;
  258.     
  259.     htmp = nil; /*if there's an error it won't get disposed*/
  260.         
  261.     if (!fileread (fnum, (long) 0, (long) sizeof (header), &header)) 
  262.         goto error;
  263.     
  264.     if (!validfileheader (header)) /*failed the reality check*/
  265.         goto error;
  266.         
  267.     if (!initeditbuffer ()) /*sets up new editbuffer*/
  268.         goto error;
  269.         
  270.     htmp = NewHandle (header.textlength); /*allocate temporary buffer*/
  271.     
  272.     if (htmp == nil) /*memory allocation error*/
  273.         goto error;
  274.         
  275.     HLock (htmp);
  276.     
  277.     if (!fileread (fnum, header.textoffset, header.textlength, *htmp)) {
  278.     
  279.         HUnlock (htmp);
  280.         
  281.         goto error;
  282.         }
  283.         
  284.     HUnlock (htmp);
  285.     
  286.     hstyle = nil; /*indicate that there is no style handle*/
  287.     
  288.     if (header.stylelength > 0) {
  289.     
  290.         hstyle = (StScrpHandle) NewHandle (header.stylelength);
  291.         
  292.         if (hstyle == nil) /*memory allocation error*/            
  293.             goto error;
  294.         
  295.         HLock ((Handle) hstyle);
  296.         
  297.         if (!fileread (fnum, header.styleoffset, header.stylelength, *hstyle)) {
  298.         
  299.             HUnlock ((Handle) hstyle);
  300.         
  301.             goto error;
  302.             }
  303.             
  304.         HUnlock ((Handle) hstyle);
  305.         } /*reading style header from disk*/
  306.         
  307.     if ((hstyle != nil) && (header.textlength > 0)) {
  308.     
  309.         HLock (htmp);
  310.         
  311.         TEStylInsert (*htmp, header.textlength, hstyle, editbuffer); 
  312.         
  313.         HUnlock (htmp);
  314.         }
  315.  
  316.     if (hstyle != nil) 
  317.         DisposHandle (hstyle);
  318.       
  319.     if (htmp != nil)  
  320.         DisposHandle (htmp); /*get rid of temporary buffer*/
  321.     
  322.     TEUpdate (&editrect, editbuffer);
  323.     
  324.     TESetSelect (header.startsel, header.endsel, editbuffer);
  325.     
  326.     TEActivate (editbuffer);
  327.     
  328.     return (true); /*the load was successful in every respect*/
  329.     
  330.     error: /*jump to here to clean up and return false*/
  331.     
  332.     if (hstyle != nil) 
  333.         DisposHandle (hstyle);
  334.       
  335.     if (htmp != nil)
  336.         DisposHandle (htmp);
  337.         
  338.     return (false);
  339.     } /*loadstylerecord*/
  340.     
  341.  
  342. StScrpHandle getscraphandle (buffer) TEHandle buffer; {
  343.     
  344.     /*
  345.     Coax TE into giving us a scrap handle for buffer.  This involves
  346.     selecting the whole buffer, calling GetStylScrap and restoring the 
  347.     selection.
  348.     */
  349.  
  350.     register int origstart, origend;
  351.     register StScrpHandle hstyle;
  352.     
  353.     origstart = (**buffer).selStart; /*save*/
  354.     
  355.     origend = (**buffer).selEnd;
  356.     
  357.     TESetSelect (0, (**buffer).teLength, buffer); /*select the whole thing*/
  358.     
  359.     hstyle = GetStylScrap (buffer);
  360.     
  361.     TESetSelect (origstart, origend, buffer); /*restore*/
  362.     
  363.     return (hstyle);
  364.     } /*getscraphandle*/
  365.     
  366.  
  367. boolean savestylerecord (fnum) int fnum; {
  368.  
  369.     /*
  370.     The caller wants us to write the contents of editbuffer out to the open file "fnum".
  371.     
  372.     This effectively insulates the caller from detailed knowledge of both the structure
  373.     of the stylerecord and the details of our file format.  It also makes it easy for
  374.     a compound style of document, that might consist of many stylerecords, to be saved.
  375.     
  376.     If we return true then the operation went smoothly.  If we return false, there
  377.     was an error in writing to disk.  
  378.     */
  379.     
  380.     register StScrpHandle hstyle;
  381.     register long ct;
  382.     register Handle h;
  383.     tystyleheader header;
  384.     
  385.     /*organize information into the header record*/
  386.     
  387.     header.versionnumber = currentversion;
  388.     
  389.     header.systemversionnumber = getsystemversion (); 
  390.     
  391.     header.vertscrollposition = 0; /*set to GetCtlValue (your vert scrollbar)*/
  392.     
  393.     SetRect (&header.windowrect, 0, 0, 0, 0);
  394.     
  395.     header.startsel = (**editbuffer).selStart;
  396.     
  397.     header.endsel = (**editbuffer).selEnd;
  398.     
  399.     header.textoffset = sizeof (header); /*starts immediately after header*/
  400.     
  401.     header.textlength = (**editbuffer).teLength;
  402.     
  403.     header.styleoffset = header.textoffset + header.textlength;
  404.     
  405.     hstyle = getscraphandle (editbuffer);
  406.         
  407.     if (hstyle == nil)
  408.         header.stylelength = 0;
  409.     else
  410.         header.stylelength = GetHandleSize ((Handle) hstyle);
  411.         
  412.     /*reserach phase is over, start writing stuff to the file*/
  413.     
  414.     if (!filewrite (fnum, (long) sizeof (header), &header)) { /*write out the header*/
  415.     
  416.         if (hstyle != nil)
  417.             DisposHandle (hstyle);
  418.         
  419.         return (false);
  420.         }
  421.     
  422.     if (header.textlength > 0) { /*there actually is some text in the buffer*/
  423.     
  424.         HLock (h = (**editbuffer).hText); /*we pass a pointer, not a handle*/
  425.     
  426.         if (!filewrite (fnum, header.textlength, *h)) { /*write out the text buffer*/
  427.         
  428.             HUnlock (h);
  429.             
  430.             if (hstyle != nil)
  431.                 DisposHandle (hstyle);
  432.         
  433.             return (false);
  434.             }
  435.             
  436.         HUnlock (h);    
  437.         }
  438.  
  439.     if (header.stylelength > 0) { /*there actually is some style information*/
  440.     
  441.         HLock (hstyle); /*must lock it down since we have to pass a pointer, not a handle*/
  442.         
  443.         if (!filewrite (fnum, header.stylelength, *hstyle)) {
  444.         
  445.             HUnlock (h);
  446.             
  447.             if (hstyle != nil)
  448.                 DisposHandle (hstyle);
  449.         
  450.             return (false);
  451.             }
  452.             
  453.         HUnlock (hstyle);
  454.         }
  455.                 
  456.     if (hstyle != nil)
  457.         DisposHandle (hstyle);
  458.         
  459.     return (true); /*the save was successful*/        
  460.     } /*savestylerecord*/
  461.  
  462.  
  463. initmacintosh () {
  464.  
  465.     /*
  466.     the magic stuff that every macintosh application needs to do 
  467.     before doing anything else.
  468.     */
  469.  
  470.     register int i;
  471.         
  472.     MaxApplZone ();
  473.     
  474.     for (i = 1; i < 11; i++) 
  475.         MoreMasters ();
  476.  
  477.     InitGraf (&thePort);
  478.     
  479.     InitFonts ();
  480.     
  481.     FlushEvents (everyEvent, 0);
  482.     
  483.     InitWindows ();
  484.     
  485.     InitMenus ();
  486.     
  487.     TEInit ();
  488.     
  489.     InitDialogs (nil);
  490.     
  491.     InitCursor();
  492.     } /*initmacintosh*/
  493.     
  494.     
  495. setupwindow () { 
  496.  
  497.     /*
  498.     set up the window that the program runs inside of.
  499.     
  500.     the window is sized to be slightly smaller that the
  501.     screen you're using.
  502.     */
  503.  
  504.     Rect r;
  505.         
  506.     r = screenBits.bounds;
  507.     
  508.     r.top += 40;
  509.     
  510.     r.bottom -= 10;
  511.     
  512.     r.left += 10;
  513.     
  514.     r.right -= 10;
  515.     
  516.     editwindow = NewWindow (nil, &r, fname, TRUE, documentProc, -1, FALSE, 0);
  517.         
  518.     SetPort (editwindow);
  519.     } /*setupwindow*/
  520.     
  521.     
  522. typedef AppFile **hdlappfile;
  523.  
  524.  
  525. int getappvnum () {
  526.  
  527.     /*
  528.     get me the volume id of the application that's running.
  529.     
  530.     useful for opening data files that are located in the same folder as
  531.     the application itself.
  532.     */
  533.     
  534.     bigstring bsname;
  535.     int refnum;
  536.     Handle hfinderparams;
  537.         
  538.     GetAppParms (bsname, &refnum, &hfinderparams);
  539.     
  540.     return ((**((hdlappfile) hfinderparams)).vRefNum);
  541.     } /*getappvnum*/
  542.     
  543.         
  544. main () {
  545.     
  546.     int vnum, fnum;
  547.     OSErr errcode;
  548.     
  549.     initmacintosh ();
  550.     
  551.     setupwindow (); /*creates a new window pointed to by editwindow*/
  552.     
  553.     editrect = (*editwindow).portRect;
  554.     
  555.     InsetRect (&editrect, 10, 10);
  556.     
  557.     vnum = getappvnum (); /*we're going to look in same folder that the program is in*/
  558.     
  559.     errcode = FSOpen (fname, vnum, &fnum);
  560.     
  561.     if (errcode != noErr) { 
  562.         
  563.         sysbeep; /*listen for a single sysbeep*/
  564.         
  565.         return;
  566.         }
  567.         
  568.     if (!loadstylerecord (fnum)) {
  569.     
  570.         sysbeep; sysbeep; /*listen for a double sysbeep*/
  571.         
  572.         FSClose (fnum);
  573.         
  574.         return;
  575.         }
  576.         
  577.     while (!Button ()) /*press the mouse button to exit*/    
  578.         
  579.         SystemTask (); /*keep TOPS, etc. active*/
  580.     
  581.     errcode = SetFPos (fnum, fsFromStart, 0); /*reset to the beginning of the file*/
  582.     
  583.     if (errcode != noErr) {
  584.     
  585.         sysbeep; /*listen for a single sysbeep*/
  586.         
  587.         FSClose (fnum);
  588.         
  589.         return;
  590.         }
  591.     
  592.     if (!savestylerecord (fnum)) {
  593.     
  594.         sysbeep; sysbeep; /*listen for a double sysbeep*/
  595.         }
  596.         
  597.     DisposeWindow (editwindow);
  598.     
  599.     FSClose (fnum);
  600.     } /*main*/
  601.